﻿using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Cpuss.Strategies.Tests
{
    /// <summary>
    /// Tests for Priority First strategy.
    /// </summary>
    [TestFixture]
    public class PriorityFirstTest
    {

        /// <summary>
        /// Check to see that the time of the simulation is as expected.
        /// </summary>
        [Test]
        public void PriorityFirstRunnerCompletionTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 21, 0),
                new Process(2, 7, 1) { Priority = Priority.High },
                new Process(3, 16, 1),
                new Process(4, 2, 15) { Priority = Priority.High },
                new Process(5, 33, 16) { Priority = Priority.Low }
            };
            Runner runner = new Runner(processLoad, new PriorityFirst(1));
            int actual = 0;

            runner.Completed += (o, e) => actual = e.TotalTime;
            runner.Run();

            Assert.AreEqual(79, actual);
        }

        /// <summary>
        /// Check to see that the metrics gathered for each process is correct.
        /// </summary>
        [Test]
        public void PriorityFirstMetricsTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 6, 0),
                new Process(2, 3, 4) { Priority = Priority.High },
                new Process(3, 1, 5) { Priority = Priority.Low },
                new Process(4, 12, 8) { Priority = Priority.High }
            };
            Runner actual = new Runner(processLoad, new PriorityFirst(1));

            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 6, 0)
            {
                StartTime = 0,
                CompletionTime = 21
            });
            expected.Add(2, new Process(2, 3, 4)
            {
                StartTime = 4,
                CompletionTime = 7
            });
            expected.Add(3, new Process(3, 1, 5)
            {
                StartTime = 21,
                CompletionTime = 22
            });
            expected.Add(4, new Process(4, 12, 8)
            {
                StartTime = 8,
                CompletionTime = 20
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that idle time in a simulation results in the correct behaviour.
        /// </summary>
        [Test]
        public void PriorityFirstIdleTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 5, 7),
                new Process(2, 6, 15),
                new Process(3, 4, 15) { Priority = Priority.High }
            };
            Runner actual = new Runner(processLoad, new PriorityFirst(1));

            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 5, 7)
            {
                StartTime = 7,
                CompletionTime = 12
            });
            expected.Add(2, new Process(2, 6, 15)
            {
                StartTime = 19,
                CompletionTime = 25
            });
            expected.Add(3, new Process(3, 4, 15)
            {
                StartTime = 15,
                CompletionTime = 19
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see the correct string is returned.
        /// </summary>
        [Test]
        public void ToStringTest()
        {
            PriorityFirst actual = new PriorityFirst(1);

            Assert.AreEqual("Priority First (Poll Time = 1)", actual.ToString());
        }

        /// <summary>
        /// Check to see that the correct data is held about each process where the poll time
        /// is greater than 1.
        /// </summary>
        [Test]
        public void PollTimeTest()
        {
            ProcessLoad processLoad = new ProcessLoad()
            {
                new Process(1, 2, 0) { Priority = Priority.High},
                new Process(2, 4, 4) { Priority = Priority.Low },
                new Process(3, 3, 1) { Priority = Priority.Medium },
                new Process(4, 7, 2) { Priority = Priority.High }
            };
            Runner actual = new Runner(processLoad, new PriorityFirst(4));
            Dictionary<int, Process> expected = new Dictionary<int, Process>();
            expected.Add(1, new Process(1, 2, 0)
            {
                StartTime = 0,
                CompletionTime = 2
            });
            expected.Add(2, new Process(2, 4, 4)
            {
                StartTime = 12,
                CompletionTime = 16
            });
            expected.Add(3, new Process(3, 3, 1)
            {
                StartTime = 9,
                CompletionTime = 12
            });
            expected.Add(4, new Process(4, 7, 2)
            {
                StartTime = 2,
                CompletionTime = 9
            });

            actual.Run();

            for (int i = 1; i <= expected.Count; i++)
            {
                Assert.AreEqual(expected[i].StartTime, actual.Metrics[i].StartTime);
                Assert.AreEqual(expected[i].CompletionTime, actual.Metrics[i].CompletionTime);
            }
        }

        /// <summary>
        /// Check to see that the correct exception is thrown when the poll time is less than 1.
        /// </summary>
        [Test]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void PollTimeLessThanOneTest()
        {
            PriorityFirst actual = new PriorityFirst(0);
        }

    }

}
